package com.miao.im.server;

import cn.hutool.extra.spring.SpringUtil;
import com.miao.im.common.Constant;
import com.miao.im.common.Message;
import com.miao.im.dispatcher.MessageDispatcher;
import com.miao.im.filter.MessageFilterContext;
import com.miao.im.model.AuthResponseModel;
import com.miao.im.server.session.SessionContext;
import com.miao.im.server.session.SessionManager;
import com.miao.im.service.auth.SocketAuthManager;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j;

/**
 * @Auther: miaoguoxin
 * @Date: 2021/2/24 17:59
 * @Description:
 */
@Slf4j
@ChannelHandler.Sharable
public class WebsocketAccessHandler extends SimpleChannelInboundHandler<BinaryWebSocketFrame> {


    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        if (!Constant.SERVER_ACTIVE) {
            log.info("服务器已关闭，拒绝连接：{}", ctx.channel().remoteAddress().toString());
            ctx.channel().close();
            return;
        }
        SessionContext sessionContext = new SessionContext(ctx);
        SessionManager.bindConnect(sessionContext);
        log.info("客户端已连接：{}", sessionContext.getRemoteNode().toString());
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        SessionContext sessionContext = SessionManager.unbind(ctx.channel());
        if (sessionContext == null) {
            return;
        }
        if (sessionContext.isAuth()) {
            AuthResponseModel.AuthResponse userInfo = sessionContext.getUserInfo();
            SocketAuthManager socketAuthManager = SpringUtil.getBean(SocketAuthManager.class);
            socketAuthManager.logout(userInfo.getUserId(), userInfo.getDeviceNumber());
        }
        log.info("客户端已断开连接：{}", sessionContext.getRemoteNode().toString());
    }


    @Override
    protected void channelRead0(ChannelHandlerContext ctx, BinaryWebSocketFrame msg) throws Exception {
        if (!Constant.SERVER_ACTIVE) {
            log.info("服务器已关闭，拒绝接收消息：{}", ctx.channel().remoteAddress().toString());
            return;
        }
        ByteBuf content = msg.content();
        try {
            Message message = Message.read(content);
            SessionContext sessionContext = SessionManager.get(ctx.channel());
            if (sessionContext == null) {
                ctx.channel().close();
                return;
            }
            //重置读空闲次数
            sessionContext.resetReadIdleTimes();
            //拦截
            MessageFilterContext messageFilterContext = SpringUtil.getBean(MessageFilterContext.class);
            if (!messageFilterContext.execute(message, sessionContext)) {
                return;
            }
            MessageDispatcher dispatcher = SpringUtil.getBean(MessageDispatcher.class);
            dispatcher.push(message, sessionContext)
                    .doOnError(ctx::fireExceptionCaught)
                    .contextWrite(context -> context.put("sessionContext",context))
                    .subscribe();
        } catch (Exception e) {
            ctx.fireExceptionCaught(e);
        }
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        Channel channel = ctx.channel();
        SessionContext sessionContext = SessionManager.get(channel);
        if (sessionContext == null) {
            ctx.channel().close();
            return;
        }
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            switch (event.state()) {
                case READER_IDLE:
                    String eventType = "读空闲";
                    if (sessionContext.incAndGetReadIdleTimes() > 3) {
                        log.debug(" [server]读空闲超过3次，关闭连接");
                        this.channelInactive(ctx);
                    }
                    break;
            }
        }
    }

}
